Technotes
|
Cross-Platform Communication Using the PC Compatibility Messaging System
This Technote describes the Messaging System Architecture used in Apple's PC Compatibility and DOS Compatibility products. Specifically, the messaging system allows communication of data between the PC-based machine running on a NuBus or PCI card and the Macintosh OS. This inter-machine communication is facilitated through a driver on the Macintosh which controls the PC card and allows it to run within the Macintosh hardware and software space. This Technote is directed toward third-party developers who are interested in developing software for the Mac and the PC which needs to communicate instructions or data between platforms. The applications developed to use the messaging system would be intended to run specifically with Apple's PC Compatibility products. This document assumes the developer is familiar with application and driver-level software development on the Macintosh platform as well as the PC. For the Mac, an understanding of the Device Manager and implementing 68K and PPC native code is essential. For the PC, an understanding of 16-bit DOS Real mode execution and x86 assembly language is useful. For development in conjunction with Window's based applications, a knowledge of 32-bit Windows programming and virtual device drivers (VxD's) is necessary. This document also assumes the reader is familiar with Apple's PC Compatibility products and how they function within the Macintosh OS. Further information on Macintosh programming at the device level can be found in Inside Macintosh: Devices . For more information on Windows VxD programming (only necessary for using the message system with Windows 3.x or Windows 95), see Writing Windows Virtual Device Drivers by David Thielen and Bryan Woodruff. The MacMsgTest and PCMsgTst ToolsThis Technote is accompanied by two tools, one for the Mac and one for the PC, that perform very simple messaging. MacMsgTest is written entirely in C and is designed and compiled to run PPC Native. PCMsgTst is written in C and x86 assembly. Source code, header files, and makefiles are included for each tool. The necessary build environments are not included. The tools are available on Apple's Developer website (http://www.developer.apple.com/) and on the Developer CD. Please see each tool's individual ReadMe files for further information on executing and building MacMsgTest and PCMsgTst. To download the tools, just click on the icon below: CONTENTS
About the PC Compatibility Messaging SystemThe PC Compatibility (or DOS Compatibility) systems currently supported by this messaging architecture are the Centris 610 DOS Compatible, PowerMac 6100/66 DOS Compatible, the Quadra 630 DOS Compatible, and any PCI-based Macintosh which includes the most recent PCI-based 100Mhz Pentium and Cyrix 5x86 PC Compatibility Cards. Currently, the only system bundled with the PCI- based cards is the PowerMac 7200/120. All of these systems must be running version 1.5 of the PC Compatibility Software or later, which includes the driver that allows the messaging system to function. The messaging system is implemented as a 16-bit DOS real-mode driver and is used extensively in these current products to allow the PC to have access to the shared devices on the Mac (HD, CD, floppy, etc.), networking communications, folder sharing, and clipboard support. Using the Messaging SystemSoftware programs on the Mac and the PC are capable of exchanging messages containing up to 64K of data by using the Messaging System API. Applications that plan on sharing messages must define and understand the types of messages to be sent and received. More importantly, verification and acknowledgment of sent and received messages must be maintained by the sending and receiving applications. The driver installed at the Mac OS startup time is called ".Symbiosis" and needs to be opened by your Macintosh application before driver calls can be made. Your program will then use device manager _Control calls to register, send, and receive messages. The PC accesses the messaging system through a software interrupt interface. The application will load x86 registers with appropriate values, a function selector, and then call the messaging system via an INT 5Fh call. Basic Messaging ConceptsBoth the Mac and the PC applications accessing the messaging systems must define a 32-bit selector for their messages and a count value that denotes the number of different types of messages available for this selector. Typically, applications that are to pass messages define one selector type. Selector types can be any unique 32-bit value, so 4-character values work well (32-bit OSType). Both the Mac and the PC applications must know the message selector and they must know the number of message types associated with that selector in order to register themselves with the messaging system (See the Registering Messages section for more details on Message Selectors and Types). The basic process of single message communication between an application on the Mac and an application on the PC is as follows:
Performing multiple message communication is also possible (i.e., the ability to send more than one message before receiving an acknowledge), but requires more maintenance. The intent here is to describe the basic communication between applications. Therefore, multiple message communication concepts are discussed in the Advanced Messaging Techniques section of this Technote. |
Opening the Messaging SystemOn the Mac, the application must open the .Symbiosis driver and retrieve the refNum for the driver in order to make other message system control calls. Your application can do this using the OpenDriver function. If this returns an error, the .Symbiosis driver is not available and the messaging system cannot be used. On the PC, the application must load the AH register with 0 and call the software interrupt INT 5Fh. If the messaging system is installed, AH = $A5 and AL will equal the highest implemented function code, which is currently 5, when the interrupt returns. The highest implemented function code means there are a total of 5 functions supported for registering and receiving messages. This will be discussed in detail later. Essential Data TypesThe basic data structures for accessing the messaging system on the Mac side are defined as follows: typedef struct { QElemPtr qLink; SInt16 qType; SInt16 ioTrap; Ptr ioCmdAddr; ProcPtr ioCompletion; // always NULL OSErr ioResult; // error result info. StringPtr ioNamePtr; SInt16 ioVRefNum; SInt16 ioCRefNum; // refNum of Symbiosis driver. SInt16 csCode; // messaging system function void * csPtr; // pointer to procedure or data SInt32 csData; // data SInt32 csData2; // data }SBParamBlockRec, *SBParamBlockRecPtr; The SBParamBlockRec is virtually the same as a standard paramBlockRec except only the fields used by the messaging system are included for the data area. The only fields needed for messaging are the ioCRefNum and csCode for calling the driver, and then the csPtr and csData fields which are used to point to other structures that are defined below. The different csCode's used for calling the messaging system are defined below: |
enum { eSendMessage = 800, // Send a message eInstallMsgHandler = 801, // Install a message handler eRemoveMsgHandler = 802, // Remove message handler eRegisterMessage = 803 // Register message type };
The data structures used for sending and receiving messages are below:
typedef struct MsgPBlk { struct MsgPBlk* msgQLink; // Pointer to next MsgPBlk SInt16 msgQType; // Queue Flags SInt16 msgCmd; // The message type or command SInt32 msgParam1; // Message parameter 1 SInt32 msgParam2; // Message parameter 2 void* msgBuffer; // Ptr to the msg data buffer SInt32 msgReqCount; // Requested data length SInt32 msgActCount; // Actual data length MsgCompletionUPP msgCompletion; // Ptr to comp. rtn. or NULL SInt16 msgResult; // The result of msg operation UInt16 msgFlags; // Message flags UInt32 msgUserData; // refCon (a5, etc) }MsgPBlk, *MsgPBlkPtr; typedef struct MsgRecElem { struct MsgRecElem* recQLink; // Next queue element SInt16 recQType; // queue flags SInt16 recFlags; // Not used...Set to zero MsgReceiveUPP recProc; // Ptr to the receive proc. SInt16 recCmdBase; // Msg Selector base. SInt16 recCmdCount; // # of msgTypes UInt32 recUserData; // refCon (could be A5...) }MsgRecElem, *MsgRecElemPtr;
The MsgPBlk is used for sending and receiving data and the MsgRecElem is used for notification of incoming messages. For the PC application using the messaging system, the PC Data structures and function ID constants are defined below: |
enum { eIsAvailable = 0 // Index for is available eSendMessage = 1 // Index for Send func eInstallMsgHandler = 2 // Index for Install Msg Handler func eRemoveMsgHandler = 3 // Index for Remove Msg Handler func eRegisterMessage = 4 // Index for Register Msg eVersionCheck = 5 // Index to get the version numbers }; // some basic types used for the MsgPBlk and MsgRecElem structures. typedef char SInt8; typedef short SInt16; typedef long SInt32; typedef unsigned char UInt8; typedef unsigned short UInt16; typedef unsigned long UInt32; typedef char __far* Ptr32; typedef struct MsgPBlk { struct MsgPBlk* link; // Pointer to the next MsgPBlk. SInt16 msgCmd; // The message command or type SInt32 msgParam1; // Param 1 SInt32 msgParam2; // Param 2 UInt32 msgCompletion; // Ptr to the completion routine Ptr32 msgBuffer; // Ptr to the data buffer SInt32 msgReqCount; // Length of the data SInt32 msgActCount; // # of bytes actually transfered SInt8 msgResult; // The err code after complete or 1 UInt8 msgFlags; // Not used, init to zero. UInt32 msgUserData; // for caller's use UInt32 msgVXD; // Used by VxD }MsgPBlk, *MsgPBlkPtr; typedef struct MsgRecElem { struct MsgRecElem* Link; SInt32 Code; SInt16 cmdBase; // the base message number for this proc SInt16 cmdCount;// the # of message numbers for this proc UInt32 userData;// for caller's use UInt32 recVXD; // reserved - Used by VxD }MsgRecElem, *MsgRecElemPtr;
Registering Messages with the Message SystemThe process of message registration requires both the Mac application and the PC application to be aware of a predefined set of message types that are defined by the application developer. Both applications are aware of the data formats of these messages and know how to decode and use certain parts of the messages based on their distinct message type ID. These message types are grouped together by a message selector (4-byte value of type OSType) known to both the Mac and the PC application. Both applications send the message selector and the number of message types to the message system and the message system returns a cmdBaseID (See Figure 1). Figure 1. Registering a message selector and message types. Once the set of messages for the Mac and PC applications has been registered with the message system, each individual message has a unique value (called a msgCmd) which ranges from the msgCmdBaseID to the total number of messages - 1. When the applications send and receive messages, they will reference particular message types through the msgCmdBaseID plus some value which specifies the message type. The resulting value is the msgCmd. Registering a Message on the MacTo register messages on the Mac, your application must fill out a SBParamBlockRec make the appropriate driver call. To do this, fill out the following fields of a SBParamBlockRec: |
--> ioCRefNum = <refNum of the .Symbiosis driver>; --> ioVRefNum = 0; --> ioCompletion = 0; <-- ioResult = 0; --> csCode = eRegisterMessage; <-> csPtr = <message selector>; --> csData = <number of message types>;
The message selector entered in the csPtr field should by a 4-byte value of type OSType. The csData field should be the number of message types registered. Make the driver call using the PBControlImmed function. If the registration is successful, the ioResult will equal noErr and the csPtr will contain a message base command (msgCmdBaseID) value which is used in the message send and receive parameter blocks. Registering a Message on the PCTo register a message on the PC, load the 32-bit message selector into the EBX register and put the number of message types in CX. Then call INT 5Fh with AH equal to the registerMessage function ID (4). On return from the interrupt, BX will contain a message command base ID which must be used in the MsgPBlk's and MsgRecElem's. A sample function called MsgRegister, which passes in a selector and count (number of msg types) and returns the command base ID, is shown below: |
MsgRegister PROC FAR C msgSel:DWORD, msgCount:WORD, msgCmmd:WORD mov ebx,msgSel ; load EBX with the msgSelector. mov cx,msgCount ; load CX with the msgCount mov ah,registerMessage ; load AH with the function ID. int 05Fh ; make the interrupt call. mov dx,bx ; move BX to DX. mov bx,msgCmmd ; Put the address of msgCmd in BX. mov [bx],dx ; Return the msgCmd value. ret MsgRegister ENDP
Constants (Mac)
#define kDriverName "\p.Symbiosis" // The name of the driver enum { eSendMessage = 800, // Send a message eInstallMsgHandler = 801, // Install a message handler eRemoveMsgHandler = 802, // Remove message handler eRegisterMessage = 803 // Register message type }; enum { msgNoError = 0, // No error msgOverrun = -1, // More data was available msgUnderrun = -2, // Less data was available msgTimeout = -3 // Timeout error }; Data Types (Mac) typedef struct { QElemPtr qLink; SInt16 qType; SInt16 ioTrap; Ptr ioCmdAddr; ProcPtr ioCompletion; // always NULL OSErr ioResult; // error result info. StringPtr ioNamePtr; SInt16 ioVRefNum; SInt16 ioCRefNum; // refNum of Symbiosis driver. SInt16 csCode; // messaging system function void * csPtr; // pointer to procedure or data SInt32 csData; // data SInt32 csData2; // data }SBParamBlockRec, *SBParamBlockRecPtr; typedef struct MsgPBlk { struct MsgPBlk* msgQLink; // Pointer to next MsgPBlk SInt16 msgQType; // Queue Flags SInt16 msgCmd; // The message type or command SInt32 msgParam1; // Message parameter 1 SInt32 msgParam2; // Message parameter 2 void* msgBuffer; // Ptr to the msg data buffer SInt32 msgReqCount; // Requested data length SInt32 msgActCount; // Actual data length MsgCompletionUPP msgCompletion; // Ptr to comp. rtn. or NULL SInt16 msgResult; // The result of msg operation UInt16 msgFlags; // Message flags UInt32 msgUserData; // refCon (a5, etc) }MsgPBlk, *MsgPBlkPtr; typedef struct MsgRecElem { struct MsgRecElem* recQLink; // Next queue element SInt16 recQType; // queue flags SInt16 recFlags; // Not used...Set to zero MsgReceiveUPP recProc; // Ptr to the receive proc. SInt16 recCmdBase; // Msg Selector base. SInt16 recCmdCount; // # of msgTypes UInt32 recUserData; // refCon (could be A5...) }MsgRecElem, *MsgRecElemPtr;Universal ProcPtr and Procedure Definitions
#if GENERATINGCFM typedef UniversalProcPtr MsgCompletionUPP; typedef UniversalProcPtr MsgReceiveUPP; #else typedef ProcPtr MsgCompletionUPP; typedef ProcPtr MsgReceiveUPP; #endif enum { uppMsgReceiveProcInfo = kRegisterBased | REGISTER_ROUTINE_PARAMETER(1, kRegisterA1, SIZE_CODE(sizeof(MsgRecElemPtr))) | REGISTER_ROUTINE_PARAMETER(2, kRegisterD0, SIZE_CODE(sizeof(short))) | REGISTER_ROUTINE_PARAMETER(3, kRegisterD1, SIZE_CODE(sizeof(long))) | REGISTER_ROUTINE_PARAMETER(4, kRegisterD2, SIZE_CODE(sizeof(long))) | REGISTER_RESULT_LOCATION(kRegisterA0) | RESULT_SIZE(kFourByteCode), uppMsgCompletionProcInfo = kRegisterBased | REGISTER_ROUTINE_PARAMETER(1, kRegisterA0, SIZE_CODE(sizeof(MsgPBlkPtr))) | REGISTER_RESULT_LOCATION(kRegisterA0) | RESULT_SIZE(kFourByteCode) }; #if GENERATINGCFM #define NewMsgReceiveProc(userRoutine) \ (MsgReceiveUPP) NewRoutineDescriptor((ProcPtr)(userRoutine), \ uppMsgReceiveProcInfo, \ GetCurrentArchitecture()) #else #define NewMsgReceiveProc(userRoutine) \ ((MsgReceiveUPP) (userRoutine)) #endif #if GENERATINGCFM #define NewMsgCompletionProc(userRoutine) \ (MsgCompletionUPP) NewRoutineDescriptor((ProcPtr)(userRoutine), \ uppMsgCompletionProcInfo, \ GetCurrentArchitecture()) #else #define NewMsgCompletionProc(userRoutine) \ ((MsgCompletionUPP) (userRoutine)) #endifNote: All of the PC constants and data types for assembly language programming can be found in the "PCMesg.inc" file that is part of the PCMsgTst tool that accompanies this Technote. Constants and data types for C programming can be found in the PCMesg.h file that is included
Constants (PC)
enum { eIsAvailable = 0 // Index for is available eSendMessage = 1 // Index for Send func eInstallMsgHandler = 2 // Index for Install Msg Handler func eRemoveMsgHandler = 3 // Index for Remove Msg Handler func eRegisterMessage = 4 // Index for Register Msg eVersionCheck = 5 // Index to get the version numbers };Data Types (PC)
// some basic types used for the MsgPBlk and MsgRecElem structures. typedef char SInt8; typedef short SInt16; typedef long SInt32; typedef unsigned char UInt8; typedef unsigned short UInt16; typedef unsigned long UInt32; typedef char __far* Ptr32; typedef struct MsgPBlk { struct MsgPBlk* link; // Pointer to the next MsgPBlk. SInt16 msgCmd; // The message command or type SInt32 msgParam1; // Param 1 SInt32 msgParam2; // Param 2 UInt32 msgCompletion; // Ptr to the completion routine Ptr32 msgBuffer; // Ptr to the data buffer SInt32 msgReqCount; // Length of the data SInt32 msgActCount; // # of bytes actually transfered SInt8 msgResult; // The err code after complete or 1 UInt8 msgFlags; // Not used, init to zero. UInt32 msgUserData; // for caller's use UInt32 msgVXD; // Used by VxD }MsgPBlk, *MsgPBlkPtr; typedef struct MsgRecElem { struct MsgRecElem* Link; SInt32 Code; SInt16 cmdBase; // the base message number for this proc SInt16 cmdCount; // the # of message numbers for this proc UInt32 userData; // for caller's use UInt32 recVXD; // reserved - Used by VxD }MsgRecElem, *MsgRecElemPtr;
SummaryThe Messaging System Architecture described in this Technote is
compatible with the PC Compatibility Software v1.5 or later which is
installable on all DOS Compatible and PC Compatible products shipped
by Apple. This includes the Centris 610 DOS Compatible, the Quadra
630 DOS Compatible, the PowerMac 6100 DOS Compatible, the 7200 PC
Compatible, and all PCI based Macintoshes which support the 12" 100
MHz Pentium and 7" 100MHz Cyrix 5x86 PC Compatibility Cards. Technotes Previous Technote | Contents | Next Technote |